#pragma once
#include <iostream>
#include <cstdio>
#include <string>
#include <ctime>
#include <sys/types.h>
#include <unistd.h>
#include <stdarg.h>
#include <pthread.h>
#include <fstream>
#include "LockGuard.hpp"
using namespace std;
bool gIsSave = false; // 此日志消息是否需要保存
const string logname = "log.txt";
// 日志等级
enum Level
{
    DEBUG = 0, // 用于测试
    Info,      // 常规消息
    WARNING,   // 警告
    ERROR,     // 错误
    FATAL      // 致命的错误
};
// 将massage日志信息写入到filename文件中
void SaveFile(const string &filename, const string &massage)
{
    ofstream out(filename, ios::app); // 追加
    if (out.is_open() == false)
    {
        // 如果打开失败就返回
        return;
    }
    // 此时打开成功,写入信息到out流中去
    out << massage;
    out.close();
}

// 将日志等级转换为字符串信息
string LevelToString(int level)
{
    switch (level)
    {
    case DEBUG:
    {
        return "Debug";
    }
    case Info:
    {
        return "Inof";
    }
    case WARNING:
    {
        return "Warning";
    }
    case ERROR:
    {
        return "Error";
    }
    case FATAL:
    {
        return "Fatal";
    }
    default:
    {
        return "Unknown";
    }
    }
}
string GetTimeString()
{
    // 获取当前时间戳
    time_t curr_time = time(nullptr);
    // 将时间戳转化为时间
    struct tm *format_time = localtime(&curr_time);
    if (format_time == nullptr)
    {
        return "None";
    }
    char time_buffer[1024];
    snprintf(time_buffer, sizeof(time_buffer), "%d年-%d月-%d日 %d时:%d分:%d秒",
             format_time->tm_year + 1900,
             format_time->tm_mon + 1,
             format_time->tm_mday,
             format_time->tm_hour,
             format_time->tm_min,
             format_time->tm_sec);
    return time_buffer;
}
// 保护打印安全
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;

// 日志格式---日志等级、日志时间、代码文件名/行数、日志内容
void LogMessage(string filename, int line, bool issave, int level, const char *format, ...)
{
    // level:日志等级
    // 将日志等级转换为字符串信息
    string levelstr = LevelToString(level);
    // 获取时间
    string timestr = GetTimeString();
    pid_t selfid = getpid();

    char buffer[1024];
    va_list arg;           // 可变参数
    va_start(arg, format); // 初始化可变参数va_start(可变参数,格式)
    vsnprintf(buffer, sizeof(buffer), format, arg);
    va_end(arg); // 置空可变参数

    string massage = "[时间:" + timestr + "]" + "[日志等级:" + levelstr + "]" + "[pid:" + to_string(selfid) + "]" + "[文件名" + filename + "]" + "[行号" + to_string(line) + "] " + buffer + "\n";
    // 打印安全
    LockGuard lockguard(&lock);
    if (issave == false)
    {
        // 不需要保存，就打印即可
        cout << "[时间:" << timestr << "]"
             << "[日志等级:" << levelstr << "]"
             << "[pid:" << selfid << "]"
             << "[文件名" << filename << "]"
             << "[行号" << line << "] "
             << buffer;
    }
    else
    {
        // 将日志信息写入到文件中
        SaveFile(logname, massage);
    }
    // cout << levelstr << " : " << timestr << " : " << filename << " : " << line << " : " << buffer << endl;
}
#define LOG(level, format, ...)                                                \
    do                                                                         \
    {                                                                          \
        LogMessage(__FILE__, __LINE__, gIsSave, level, format, ##__VA_ARGS__); \
    } while (0)
#define EnableFile()    \
    do                  \
    {                   \
        gIsSave = true; \
    } while (0) // 将日志信息保存到文件
#define EnableScreen()   \
    do                   \
    {                    \
        gIsSave = false; \
    } while (0) // 将日志信息输出到显示器
// 拿可变参数举例---假设默认传递进来的参数都是整数，假设三个可变参数
// 栈帧
//------------//
// 可变参数3---//
// 可变参数2---//
// 可变参数1---//
// num--------//
// -----------//
// 这也解释了printf("xxxxx-%d-%c",int,char);%d-%c必须与后面的数据类型int,char匹配上,传递类型本质就是为了做指针的移动
void Test(int num, ...)
{
    va_list arg;        // 取num的地址
    va_start(arg, num); // 通过num变量取可变参数1的地址-类似arg=&num-sizeof(int*);
    while (num > 0)
    {
        // 一步一步拿到各个可变参数
        int data = va_arg(arg, int); // int data=*((int*)arg);arg-=sizeof(int*);
        cout << "data:" << data << endl;
        num--;
    }
    va_end(arg); // arg=nullptr;
}
